package br.museu.dao.generico;

import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;

import br.museu.dao.generico.interfaces.IDAOGenerico;

public abstract class DAOGenerico<Entidade> implements IDAOGenerico<Entidade> {

	protected Class<Entidade> classePersistente;

	@Autowired
	private SessionFactory sessionFactory;

	@SuppressWarnings("unchecked")
	public DAOGenerico() {
		ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
		classePersistente = (Class<Entidade>) parameterizedType.getActualTypeArguments()[0];
	}

	/**
	 * Executa o merge do objeto que se encontra em mem�ria.
	 * 
	 * @param objeto
	 *            a ser realizado o merge
	 * @return objeto que foi executado o merge
	 */
	@SuppressWarnings("unchecked")
	public final void alterar(Entidade objeto) {
		try {
			objeto = (Entidade) getCurrentSession().merge(objeto);

			getCurrentSession().flush();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Salva o objeto atual na base de dados.
	 * 
	 * @param objeto
	 *            a ser salvo
	 */
	public final void inserir(Entidade objeto) {
		try {
			getCurrentSession().persist(objeto);

			getCurrentSession().flush();
			System.out.println(classePersistente.getSimpleName() + " salvo com sucesso");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Salva o objeto atual na base de dados.
	 * 
	 * @param objeto
	 *            a ser salvo
	 */
	public final void inserirColecao(Collection<Entidade> colecao) {
		try {
			for (Entidade entidade : colecao) {
				getCurrentSession().persist(entidade);
			}
			getCurrentSession().flush();
			System.out.println(classePersistente.getSimpleName() + " salvos com sucesso: " + colecao.size());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Remove o objeto da base de dados.
	 * 
	 * @param objeto
	 *            a ser removido
	 */
	@SuppressWarnings("unchecked")
	public final void remover(Entidade objeto) {
		try {
			objeto = (Entidade) getCurrentSession().merge(objeto);
			getCurrentSession().delete(objeto);
			getCurrentSession().flush();

			System.out.println(classePersistente.getSimpleName() + " removido com sucesso");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Busca o objeto uma vez passado sua chave como par�metro.
	 * 
	 * @param chave
	 *            identificador
	 * @return Objeto do tipo T
	 */
	@SuppressWarnings("unchecked")
	public final Entidade consultarPorId(Integer chave) {
		Entidade instance = null;
		try {
			instance = (Entidade) getCurrentSession().get(classePersistente, chave);
		} catch (RuntimeException re) {
			re.printStackTrace();
		}
		return instance;
	}

	@SuppressWarnings("unchecked")
	public List<Entidade> consultarTodos() {

		try {
			Criteria criteria = getCurrentSession().createCriteria(classePersistente);
			return criteria.list();
		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}

	@SuppressWarnings("unchecked")
	public List<Entidade> consultarTodos(Integer indiceInicial, Integer quantidade) {
		try {
			String sql = "from " + classePersistente.getSimpleName();
			Query query = getCurrentSession().createQuery(sql);
			query = query.setFirstResult(indiceInicial).setMaxResults(quantidade);
			return query.list();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public Session getCurrentSession() {
		return sessionFactory.getCurrentSession();
	}

}
